<?php
/**
 * RedCross Application
 *
 * @copyright Copyright (c) 2010, Kristian Stokke Tryggestad, Rune Vikestad, Andreas Flaten Wist, Simen Nicolai Echholt, Ole Henrik Jahren
 */

/**
 * ServiceAbstract service class that implements common methods and variables
 *
 * @category    RedCross
 * @package     RedCross_Service
 */
abstract class RedCross_Service_ServiceAbstract implements RedCross_Service_ServiceInterface, Zend_Acl_Resource_Interface
{

    protected $_gateway;
    protected $_acl;
    protected $_role;
    protected $_throwAclException = true;

    /**
     * Name of the model that belongs to the service
     * @var string
     */
    protected $_model;

    public function __construct(RedCross_Gateway_GatewayInterface $gateway, $model)
    {
        $this->setGateway($gateway);
        $this->setModel($model);
    }

    /**
     * Retrieves the name of the model that belongs to the service
     */
    protected function getModel()
    {
        return $this->_model;
    }

    protected function setModel($model)
    {
        $this->_model = $model;
    }

    public function setGateway($gateway)
    {
        $this->_gateway = $gateway;
    }

    public function getGateway($name = null)
    {
        if (!is_null($name)) {
        	return new $name();
        }
        return $this->_gateway;
    }

    public function setAcl(Zend_Acl $acl)
    {
        $this->_acl = $acl;
        return $this;
    }

    public function getAcl()
    {
        return $this->_acl;
    }

    public function setRole($role = null)
    {
        if (is_array($role)) {
            if (!isset($role['role'])) {
                $role['role'] = RedCross_Acl::GUEST_NAME;
            }
            $role = new Zend_Acl_Role($role['role']);
        } elseif (is_string($role)) {
            $role = new Zend_Acl_Role($role);
        } elseif (null === $role) {
            $role = new Zend_Acl_Role(RedCross_Acl::GUEST_NAME);
        } elseif (!$role instanceof Zend_Acl_Role_Interface) {
            throw new Exception('Invalid identity provided');
        }
        $this->_role = $role;
        return $this;
    }

    public function getRole()
    {
        if ($this->_role === null) {
            $this->setRole();
        }
        return $this->_role;
    }

    public function getResourceId()
    {
        $class = get_class($this);
        $service = explode('_', $class);
        $service = array_pop($service);
        $service = (string)(strtolower(substr($service, 0, 1)) . substr($service, 1));
        return 'service:' . $service;
    }

    // Disable throwing of exception on the next checkAcl-call
    public function disableAclException()
    {
        $this->_throwAclException = false;
    }

    public function checkAcl($action, $throwException = true)
    {
        $allowed = $this->getAcl()->isAllowed($this->getRole(), $this, $action);
//        echo 'Was ' . $this->getRole()->getRoleId() . ' allowed to access ' . $this->getResourceId() . ' with the privilege ' . $action . ': ' . ($allowed === true ? 'Yes' : 'No');
        if (!$allowed && $throwException) {
            // Special case when something external to the service wants the service to not
            // throw exception on this function call
            if (!$this->_throwAclException) {
            	$this->_throwAclException = true;
            	return $allowed;
            }
        	throw new RedCross_Exception_Unauthorized('Insufficient rights ------ ' . 'Was ' . $this->getRole()->getRoleId() . ' allowed to access ' . $this->getResourceId() . ' with the privilege ' . $action . ': ' . ($allowed === true ? 'Yes' : 'No'));
        }
        return $allowed;
    }

    /**
     * Attempts to save the given model or array to persistent storage
     *
     * @param mixed $data
     * @return mixed
     */
    public function save($data)
    {
        //$this->checkAcl('save');    should this function be made private? to avoid having to check 'save' premissions...
	
        $class = $this->getModel();
	
        if (is_array($data)) {
        	$data = new $class($data);
        }

        if (!is_object($data) || !$data instanceof $class) {
            throw new RedCross_Service_Exception("Incorrect datatype. Should be an array or an object of type {$this->getModel()}");
        }

        return $this->getGateway()->save($data);
    }

    public function fetch($id)
    {
    	if (!is_int($id)) {
    		throw new RedCross_Service_Exception('The given argument should be an integer');
    	}
    	return $this->_gateway->fetch($id);
    }

    public function fetchAll()
    {
    	return $this->_gateway->fetchAll();
    }

    public function delete($data)
    {
    	$class = $this->getModel();

    	if (is_int($data))
    	{
    		$data = $this->fetch($data);
    	}
    	if (is_array($data))
    	{
    		$data = new $class($data);
    	}
        if (!is_object($data) || !$data instanceof $class) {
        	throw new RedCross_Service_Exception("Incorrect datatype on the given argument. Should be an array, int or an object of type {$this->getModel()}");
        }
        return $this->_gateway->delete($data);
    }
}
